/*Author: Andy Wu, Matthew Lorenzo Assignment: Lab1, Calculator*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

/*problems with registry operations using 3 arguments(has been fixed as of 8PM of 01/19/2011);
 still working on remembering registry history part -AW*/
/*registry history part added by Matt, error in storing reg hist in readreg function, i.e. keeps reinitializing registers to 0*/
/*registry history seemingly fixed in collaboration(5PM 01262012)*/

/*Functions for operations*/

double binplus(double x,double y)
	{
	return x+y;
	}

double binmin(double x, double y)
	{
	return x-y;
	}
double unplus(double x)
	{
	return +x;
	}
double unmin(double x)
	{
	return -x;
	}
double mult(double x,double y)
	{
	return x*y;
	}
double Div(double x, double y)
	{
	return x/y;
	}

/****Setting up access to registers*****/
/*This function makes sure the character used in the register is a valid reprensation of the register, returns 1 if it is, returns 0 if not*/
int isregister(char c)
	{
	int regnum;
	regnum=(int)c;
	if ((regnum>64)&&(regnum <123))
    		{
		return 1;
		}
	else
		{
		return 0;
		}
	}

/*This function takes a character for its ASCII value and then returns a value from 0 to 25, to be used to set the location of the register in the array */
int registerpos(char c)
	{
	int regnum; /*declare var of type int to temporarily assign one ascii value to represent the register*/ 
	regnum = (int)c;
	if ((regnum>64) && (regnum<91))
		{
		regnum=regnum-65; /* converting the ascii value to lowercase */
		return regnum;
		}
	else     /*If register is capitalized*/
		{
		regnum=regnum-97;
		return regnum;
		}
	}


int mHist()
	{
	FILE *Hist;/*Reghist*/
	Hist=fopen("history.txt","r");	/*attempts to open history.txt*/
	if(Hist==0)	/*tests if history.txt file exists and prints error message if it isn't*/
		{
		printf("No existing history of commands.\n");
		return 1;
		}
	else if((fgetc(Hist))==EOF)	/*condition for if nothing is in the file*/
		{
		return 0;
		}
	else
		{
		if(read_history("history.txt"))	/*check if txt file is read successfully*/
			{
			printf("Error while reading history file.\n");
			return 2;
			}
		return 0;
		}
	}

int main(void)
	{
	double reg[26]={0};	/* array used to store the values of the register*/
	double ACC=0;		/*declare and initializes variable to accumulate values*/
	int val1=0,val2=0,arg=0;	/*declare and initializes variables to store first numerical input, store second
					numerical input, and number of arguments given by user, respectively*/
	char *cp;		/*string to store user inputs to be stored in history.txt*/
	char op[2];		/*char variable to store operators*/
	char regchar[2];	/*char variable to store register*/
	int regpos;		/*the position in register array as an int*/
	mHist();		/*call on function to take care of stored history*/
	
	/*saving register values initialization part*/
	FILE *fp;
	fp=fopen("registers.txt","r");
	if(fp)		/*if file exists, read value from file to registers*/
		{
		fread(reg,8,26,fp);
		fclose(fp);
		}

	while(1)		/*infinite loop for calculator to run*/
		{
		if(ACC==(int)ACC)	/*check if value stored in ACC is of type int*/
			{
			printf("%d",(int)ACC);	/*print stored value from accumulator variable as an int*/
			}
		else
			{
			printf("%4.2f",ACC);
			}
        
		cp=readline(":");	/*prints the ":" and copy the user input as a string into a variable*/
		if (cp!=NULL)	/*if there is an input, store it into history and write it onto history.txt*/
			{
			add_history(cp);
			write_history("history.txt");
			}
        
		arg=sscanf(cp,"%s %s %d",regchar,op,&val2);	/*tries to check for arguments in form: 'x = 5'*/
		if(arg!=3)
			{
			arg=sscanf(cp,"%d %s %d",&val1,op,&val2);/*gets the number of arguments given by user*/
			}
        
		if (arg==EOF)		/*in the case of error in reading argument, ends program*/
			{
			free(cp);
			break;
			}
		else if((val1==0)&&(arg<3))
			{
			arg=sscanf(cp,"%s %d",op,&val2);
			
			/*if statement to check if only a register variable is entered*/
			if(arg==1)
				{
				sscanf(cp,"%s",regchar);
				if(isregister(regchar[0]))
					{
					/*readreg(reg);*/
					regpos=registerpos(regchar[0]);
					ACC=reg[regpos];
					/*writereg(reg);*/
					}
				
				else if(strcmp(regchar,"-")==0)	/*when only '-' is entered, value is ACC becomes negative of original ACC value*/
					{
					arg=sscanf(cp,"%s %s",op,regchar);	/*make sure arg is only 1 and not 2*/
					if(arg==1)
						{
						ACC=unmin(ACC);
						}
					}
				}
			
			if(strcmp(op,"0")!=0)	/*case when only two arguments are given and operator is not "0"*/
				{
				if((strlen(op)>1)&&(val2==0))/*print error statement when input is entered incorrectly*/
					{
					printf("Invalid input! Remember to enter spaces between binary operators and numbers, where the numbers entered will be treated as integers, and be sure to write both numbers if performing a binary operation.\n");
					}
                
				else if(strcmp(op,"@")==0) /*check for '@' operator, for storing ACC in register*/ 
					{
					sscanf(cp,"%s %s",op,regchar);
					if((strlen(regchar)==1)&&(isregister(regchar[0])))/*checking if registry operation arguments are entered correctly*/
						{
						regpos=registerpos(regchar[0]);
						reg[regpos]=ACC;
						}
					else
						{
						printf("Invalid Registry value! Specify a registry between a-z and keep a space between @ and the register.\n");
						}
					}				
				else if(strcmp(op,"+")==0)
					{
					sscanf(cp,"%s %s",op,regchar);	/*testing if a registry letter has been used in argument*/
					if((isregister(regchar[0])))
						{
						regpos=registerpos(regchar[0]);
						ACC=binplus(ACC,reg[regpos]);
						}
					else
						{
						ACC=binplus(ACC,val2);
						}
 					}
				else if(strcmp(op,"-")==0)
					{
					sscanf(cp,"%s %s",op,regchar);
					if((isregister(regchar[0])))
						{
						regpos=registerpos(regchar[0]);
						ACC=binmin(ACC,reg[regpos]);
						}
					else
						{
						ACC=binmin(ACC,val2);
						}
					}
				else if(strcmp(op,"*")==0)
					{
					sscanf(cp,"%s %s",op,regchar);
					if((isregister(regchar[0])))
						{
						regpos=registerpos(regchar[0]);
						ACC=mult(ACC,reg[regpos]);
						}
					else
						{
						ACC=mult(ACC,val2);
						}
					}
				else if(strcmp(op,"/")==0)
					{
					sscanf(cp,"%s %s",op,regchar);
					if((isregister(regchar[0])))
						{
						regpos=registerpos(regchar[0]);
						ACC=Div(ACC,reg[regpos]);
						}
					else if(val2==0)
						{
						printf("Error: cannot divide by zero. Please try again.\n");
						}
					else 
						{
						ACC=Div(ACC,val2);
						}
					}
				free(cp);
				}
			else	/*case when 0 is the only given argument*/
				{
				arg=sscanf(cp,"%d",&val2);
				free(cp);
				ACC=val2;
				}
			}
		else if((val2==0)&&(arg==2))
			{
			printf("Invalid input! Remember to enter spaces between binary operators and numbers, where the numbers entered will be treated as integers, and be sure to write both numbers if performing a binary operation.\n");
			}
		else if((val2==0)&&(arg==1))	/*when only 0 is entered, store it as value of ACC*/
			{
			arg=sscanf(cp,"%d",&val2);
			free(cp);
			ACC=val2;
			}
		else if(arg==3)	/*when 3 arguments are entered*/
			{
            
			/*register part with 3 arguments (i.e. y = 5)*/
			if((arg==3)&&(strcmp(op,"=")==0))
				{	
				if((strlen(regchar)==1)&&(isregister(regchar[0])))
					{
					regpos=registerpos(regchar[0]);
					reg[regpos]=val2;
					}
				else
					{
					printf("Error: To set a value to a registry, use the format 'register = number'\n");
					}
				}
            
			if((strcmp(op,"+"))==0)
				{
				ACC=binplus(val1,val2);
				}
			else if((strcmp(op,"-"))==0)
				{
				ACC=binmin(val1,val2);
				}
			else if(strcmp(op,"*")==0)
				{
				ACC=mult(val1,val2);
				}
			else if(strcmp(op,"/")==0)
				{
				if(val2==0)
					{
					printf("Error: cannot divide by zero. Please try again.\n");
					}
				else 
					{
					ACC=Div(val1,val2);
					}
				}
			}
		else
			{
			printf("Invalid input! Remember to enter spaces between binary operators and numbers, where the numbers entered will be treated as integers, and be sure to write both numbers if performing a binary operation.\n");
			}
		
		/*reset values of variable to zero before loop restarts to await for next user input*/
		val1=0;
		val2=0;
		arg=0;
		op[0]=0;
		
		/*part to write register values into registers.txt file*/
		fp=fopen("registers.txt","w");
		fwrite(reg,8,26,fp);
		fclose(fp);
		}
	return 0;
	}
